<?php
/*
 * @Author: LDY dong2406@126.com
 * @Date: 2022-08-16 17:17:51
 * @LastEditors: 搬铁的码农 dong2406@126.com
 * @LastEditTime: 2025-05-28 16:34:50
 * 
 * $form->saving(function(Form $form){xxxxxx}) 注册保存之前的闭包
 * $form->saved(function(Form $form){xxxxxxx}) 注册保存之后的闭包
 * $form->bUpdate(function(Form $form){xxxxx}) 注册更新之前的闭包
 * $form->aUpdate(function(Form $form){xxxxx}) 注册更新之后的闭包
 */

namespace Ldy\Lib;

use Ldy\Traits\HasHooks;
use Ldy\Traits\Form\Config;
use Ldy\Traits\Form\Main;
use Ldy\Traits\Form\Element\Cascader;
use Ldy\Traits\Form\Element\InputNumber;
use Ldy\Traits\Form\Element\Rows;
use Ldy\Traits\Form\Element\ObjectGroup;
use Ldy\Traits\Form\Element\trees;
use Ldy\Traits\Form\Element\UploadFiles;
use Ldy\Traits\Form\Element\Textarea;
use Ldy\Traits\Form\Element\ArrayAdd;
use Ldy\Traits\Form\Element\WangEditor;
use Ldy\Traits\Form\Element\Input;
use Ldy\Traits\Form\Common;
use Ldy\Model;
use Closure;

class Form{

    use HasHooks;
    use Main;
    use Config;
    use Common;
    use Cascader;
    use InputNumber;
    use Rows;
    use ObjectGroup;
    use trees;
    use UploadFiles;
    use Textarea;
    use ArrayAdd;
    use WangEditor;
    use Input;

    /**
     * 配置保存
     *
     * @var array
     */
    protected $form = [
        "is_group"=>false,
        "rule"=>[],
        "options"=>[
            
        ]
    ];

    /**
     * 元素默认配置
     * http://www.form-create.com/v2/element-ui/
     */
    protected $rule = [
        "type"=>"input",
        // "field"=>"字段名",
        // "title"=>"标题",
        "props"=> [
            // "type"=> "text",
        ],
        "effect"=>["required"=>false],
        "validate" => []
    ];

    /**
     * option 默认配置
     *
     * @var array
     */
    protected $options = [
        "resetBtn" => false,
        "submitBtn" => false,
        "form" => [
            "labelPosition" => "right",
            "labelWidth" => "100px",
            "size"=> "small",// medium mini small
        ]
    ];

    /**
     * 元素配置指针，当前字段名
     *
     * @var string
     */
    protected $currFieldName = '';

    /**
     * 唯一字段
     *
     * @var array
     */
    protected $uniqueFields = [];

    /**
     * 对象组 元素集合
     *
     * @var array
     */
    protected $objectGroup = [];

    /**
     * 对象组
     * 闭包里面的元素字段名加上 父名称。防止重名覆盖
     *
     * @var string
     */
    protected $objectField = '';


    /**
     * 可新增的数组控件字段元素
     *
     * @var array
     */
    protected $arrayElement = [];

    
    /**
     * 数据表模型
     *
     * @var [type]
     */
    protected $model;

    /**
     * 表单组
     *
     * @param array
     */
    protected $formGroup = [];

    /**
     * 联动控制元素
     *
     * @var array
     */
    protected $controlElement = [];


    protected $resources = [];

    protected $isMobile = false;

    /**
     * 数据ID
     *
     * @var integer
     */
    public $id = 0;


    public function __construct(Model $model, Closure $callback = null){

        $this->isMobile = request()->header('MOBILE');
        
        if($this->isMobile) $this->options['form']['labelPosition'] = 'left';

        $this->form["options"] = $this->options;
        $this->model = $model;

        $this->id = request()->get('id');
        if($this->id) $this->resources = $callback ? $callback($model, $this->id):  $this->model = $this->model->find($this->id);
        if(is_object($this->resources)) $this->resources = $this->resources->toArray();

        // print_r($this->resources);
    }
    
    /**
     * 获取数据库的数据
     *
     * @param String $fieldName
     * @return void
     */
    public function getFormData(String $fieldName = ''){
        if(empty($fieldName)){
            return $this->resources;
        }else if(isset($this->resources[$fieldName])){
            return $this->resources[$fieldName];
        }
        return null;
    }

    /**
     * 读取数据库数据 赋值到表单
     *
     * @param [type] $data
     * @param [type] $key
     * @return $this
     */
    public function setFormData($data, $key= ''){
        $str = json_encode($data);
        $data = json_decode($str, true);

        if($key && isset($data[$key])){
            $this->resources = $data[$key];
        }else{
            $this->resources = $data;
        }
        
        return $this;
    }

    /**
     * https://www.wangeditor.com/
     *
     * @param String $fieldName
     * @param string $title
     * @return $this
     */
    public function wangEditor(String $fieldName, String $title=''){
        $this->__addRule($fieldName, $title, ["type"=>"QsWangEditor"]);
        $this->setEditToolbarExcludeKeys(['group-video'])
        ->setMenuConfUploadImage(["server"=> '/admin/files/upload',"headers"=>["Save-Folder"=>'editor']]);
        return $this;
    }

    /**
     * icon图标选择器
     * props 可设置属性
     * apiUrl: "/intellect/answer" 
     * drawer: false     选取器是否使用右抽屉弹出
     * field: "title"    显示行数据中那个字段数据 前端默认 name
     * title: "问卷选择"  弹窗标题
     * val: ""           显示字段值
     * width: "50%"      弹窗宽度
     * eventBtnIcon:''   自定义按钮icon，事件逻辑前端绑定 onEventBtnClick
     * @param String $fieldName
     * @param String $title
     * @return $this
     */
    public function contentSelect(String $fieldName, String $title = ''){
        $this->__addRule($fieldName, $title, ["type"=>"QsContentSelect"]);
        return $this;
    }
    /**
     * 有新增按钮的数组元素
     *
     * @return $this
     */
    public function arrayAdd(...$param){
        $title = '';
        if(count($param) == 2){
            [$fieldName, $callback] = $param;
        }else{
            [$fieldName, $title, $callback] = $param;
        }
        
        //清空对象组 元素数组
        $this->arrayElement = [];
        //闭包里面的元素字段名加上 父名称。防止重名覆盖
        $this->objectField = $fieldName;
        //执行闭包
        call_user_func($callback, $this);
        
        //执行完闭包后清空
        $this->objectField = '';

        //通过差集剔除对象组的元素
        $formRule = array_diff_key($this->form['rule'], $this->arrayElement);
        
        //通过交集找出对象组的元素,$this->arrayElement中的rule是不完整的，所以元素配置都在$this->form['rule']
        $object_rule = array_intersect_key($this->form['rule'], $this->arrayElement);
        
        $rule = [];
        // print_r($object_rule);
        foreach($object_rule as $element) {
            if(isset($element['field'])) $element['field'] = str_replace("{$fieldName}.", '',$element['field']);
            $rule[] = $element;
        }

        $this->form['rule'] = empty($formRule) ? []:$formRule;

        $this->__addRule($fieldName, $title, ["type"=>"group",'props'=>['rules'=>$rule]]);
        
        return $this;
    }
    
    public function tips($title, $content, $type="success",$effect = 'light'){

        $fieldName = '__tips_'.rand(1000,9999);
        
        $this->__addRule($fieldName,'',["type"=>'el-alert',
            "props"=>['title'=>$title,'description'=>$content,'type'=>$type, 'effect'=>$effect]
        ]);

        return $this;
    }

    public function upload(String $fieldName, String $title = '', String $uploadUrl="/admin/files/upload"){
        $this->__addRule($fieldName, $title, ["type"=>"upload", "props"=>[
            "type"=>"select",
            "uploadType"=>"image",
            "multiple"=>false,
            "name" =>$fieldName,
            "action"=> $uploadUrl,
            "limit"=>1
            ]
        ]);
        return $this;
    }
    /**
     * 树目录组件
     *
     * @param String $fieldName
     * @param [type] ...$param
     * @return $this
     */
    public function trees(String $fieldName, ...$param){
        $title = '';
        if(count($param) == 2){
            [$title, $data] = $param;
        }else{
            [$data] = $param;
        }

        $this->__addRule($fieldName, $title, ["type"=>"tree","props"=>["data"=>$data]]);
        
        return $this;
    }
    /**
     * 分组
     *
     * @param String $title
     * @param Closure $callback
     * @return $this
     */
    public function group(String $title, Closure $callback, String $name = ''){

        $this->form['is_group'] = true;
        
        $group = ["title" => $title,"rule"=>[], 'name'=>$name];

        //闭包执行前的元素配置
        $beforeRule = $this->form['rule'];
        //清空行数据
        $this->rows = [];

        call_user_func($callback, $this);

        if(!empty($this->rows)){
            $group['rule'] = $this->rows;
        }else{
            if(empty($beforeRule)){
                $res = $this->form['rule'];
            }else{
                //通过差集找出闭包执行后添加的
                $res = array_diff_key($this->form['rule'], $beforeRule);
            }
            
            $tmpRule = [];
            foreach($res as $item) $tmpRule[] = $item;
            $group['rule'] = $tmpRule;
        }
        
        $this->formGroup[] = $group;

        return $this;
    }
    /**
     * 对象元素
     *
     * @param string $fieldName 字段名称 必须在第一位传参
     * @param string $title     标题 可以不传
     * @param Closure $callback 回调函数 没标题在第二位传参
     * @return $this
     */
    public function object(...$param){

        $title = '';
        if(count($param) == 2){
            [$fieldName, $callback] = $param;
        }else{
            [$fieldName, $title, $callback] = $param;
        }
        
        //清空对象组 元素数组
        $this->objectGroup = [];
        //闭包里面的元素字段名加上 父名称。防止重名覆盖
        $this->objectField = $fieldName;
        //执行闭包
        call_user_func($callback, $this);
        
        //执行完闭包后清空
        $this->objectField = '';

        //通过差集剔除对象组的元素
        $formRule = array_diff_key($this->form['rule'], $this->objectGroup);
        
        //通过交集找出对象组的元素,$this->objectGroup中的rule是不完整的，所以元素配置都在$this->form['rule']
        $object_rule = array_intersect_key($this->form['rule'], $this->objectGroup);
        
        $rule = [];
        // print_r($object_rule);
        foreach($object_rule as $element) {
            if(isset($element['field'])) $element['field'] = str_replace("{$fieldName}.", '',$element['field']);
            $rule[] = $element;
        }

        $this->form['rule'] = empty($formRule) ? []:$formRule;

        $this->__addRule($fieldName, $title, ["type"=>"object",'props'=>['rule'=>$rule]]);

        return $this;
    }
    /**
     * 隐藏字段
     *
     * @param String $fieldName
     * @param String $title
     * @return $this
     */
    public function hidden(String $fieldName, String $title = ''){
        $this->__addRule($fieldName, $title, ["type"=>"hidden"]);
        return $this;
    }

    /**
     * icon图标选择器
     *
     * @param String $fieldName
     * @param String $title
     * @return $this
     */
    public function icons(String $fieldName, String $title = ''){
        $this->__addRule($fieldName, $title, ["type"=>"QsIconSelect"]);
        return $this;
    }

    
    /**
     * 线条
     *{
     *   "type": "el-divider",
     *   "props": {
     *      "direction": "horizontal|vertical",
     *      "contentPosition": "left | center | right" //文字位置
     *   },
     *   "wrap": {
     *       "show": false
     *   },
     *   "native": false,
     *   "children": ['文字'],
     *   "hidden": false,
     *   "display": true
     * }
     * @return $this
     */
    public function divider(String $title = ''){
        $fieldName = '__divider_'.rand(1000,9999);
        $this->__addRule($fieldName, '', ["type"=>"el-divider", "children"=>[$title],'display'=>true]);
        return $this;
    }
    /**
     * 单选
     *
     * @param String $fieldName
     * @param String $title
     * @return $this
     */
    public function radio(String $fieldName, String $title = ''){
        $this->__addRule($fieldName, $title, ["type"=>"radio"]);
        return $this;
    }

    /**
     * 多选框
     *
     * @param String $fieldName
     * @param String $title
     * @return $this
     */
    public function checkbox(String $fieldName, String $title = ''){
        $this->__addRule($fieldName, $title, ["type"=>"checkbox"]);
        return $this;
    }

    /**
     * 下拉框选择
     *
     * @param String $fieldName
     * @param String $title
     * @return $this
     */
    public function select(String $fieldName, String $title = ''){
        $this->__addRule($fieldName, $title, ["type"=>"select"]);
        return $this;
    }

    /**
     * 开关
     *
     * @param String $fieldName
     * @param String $title
     * @return $this
     */
    public function switch(String $fieldName, String $title = ''){
        $this->__addRule($fieldName, $title, ["type"=>"switch","props"=>["activeValue"=>1,"inactiveValue"=>0]]);

        return $this;
    }

    /**
     * 多级选择器
     * http://www.form-create.com/v2/element-ui/components/cascader.html
     *
     * @param String $fieldName
     * @param String $title
     * @return $this
     */
    public function cascader(String $fieldName, String $title = ''){
        $this->__addRule($fieldName, $title, ["type"=>"cascader"]);
        return $this;
    }

    /**
     * 数字输入框
     *
     * @param String $fieldName
     * @param String $title
     * @return $this
     */
    public function number(String $fieldName, String $title = ''){
        $this->__addRule($fieldName, $title, ["type"=>"InputNumber","props"=>["type"=>"number"]]);
        return $this;
    }

    /**
     * 文本域
     *
     * @param [string] $fieldName
     * @param [string] $title
     * @return $this
     */
    public function textarea(String $fieldName, String $title = ''){
        
        $this->__addRule($fieldName, $title, ["props"=>["type"=>"textarea"]]);

        return $this;
    }

    /**
     * 文本输入框
     *
     * @param String $fieldName
     * @param String $title
     * @return $this
     */
    public function text(String $fieldName, String $title = ''){

        $this->__addRule($fieldName, $title);
        
        return $this;
    }

    public function password(String $fieldName, String $title = ''){
        $this->__addRule($fieldName, $title, ["props"=>["type"=>"password"]]);

        return $this;
    }
}